package com.trade.okxserver.controller;

import com.google.common.collect.Lists;
import com.trade.okxserver.api.PublicData;
import com.trade.okxserver.enums.TimeRange;
import com.trade.okxserver.manage.SecondKlineCache;
import com.trade.okxserver.req.KLineReq;
import com.trade.okxserver.res.PublicDataRes;
import com.trade.okxserver.res.RealKlineRes;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@RestController
@RequestMapping
public class KlineController {

    @Resource
    private PublicData publicData;

    @GetMapping("getCoinType")
    public Mono<List<Map<String, String>>> getCoinType() {
        return Mono.just(publicData.instruments(null).stream()
                .map(item -> {
                    Map<String, String> objectObjectHashMap = new HashMap<>();
                    objectObjectHashMap.put("key", item.getInstId());
                    objectObjectHashMap.put("label", item.getInstId());
                    return objectObjectHashMap;
                }).toList());
    }


    @GetMapping("kline")
    public Mono<RealKlineRes> kline(@RequestParam("range") Long range, @RequestParam("symbol") String symbol) {
        RealKlineRes res = new RealKlineRes();
        RealKlineRes.DataDTO dataDTO = new RealKlineRes.DataDTO();
        TimeRange timeRange = TimeRange.getByMilliSecond(range);
        if (timeRange.equals(TimeRange.H1)) {
            List<List<Double>> lists = SecondKlineCache.get(symbol);
            dataDTO.setLines(extracted(lists, 5));
        } else if (timeRange.equals(TimeRange.H2)) {
            List<List<Double>> lists = SecondKlineCache.get(symbol);
            dataDTO.setLines(extracted(lists, 10));
        } else if (timeRange.equals(TimeRange.H4)) {
            List<List<Double>> lists = SecondKlineCache.get(symbol);
            dataDTO.setLines(extracted(lists, 15));
        } else {
            dataDTO.setLines(publicData.kLine(new KLineReq(symbol, timeRange.getLabel(), null, null, "300")));
        }
        res.setData(dataDTO);
        return Mono.just(res);
    }

    @GetMapping("instruments")
    public Mono<Object> kline( @RequestParam(value = "instId",required = false) String instId) {
        return Mono.just(publicData.instruments(instId));
    }

    private static List<List<Double>> extracted(List<List<Double>> lists, int size) {
        List<List<List<Double>>> partition = Lists.partition(lists, size);
        return partition.stream()
                .map(item -> {
                    BigDecimal min = new BigDecimal(Double.MAX_VALUE);
                    BigDecimal max = new BigDecimal(Double.MIN_VALUE);
                    BigDecimal vol = new BigDecimal(0.0);
                    BigDecimal volCcy = new BigDecimal(0.0);
                    BigDecimal volCcyQuote = new BigDecimal(0.0);
                    BigDecimal open = new BigDecimal(item.get(0).get(1));
                    BigDecimal close = new BigDecimal(item.get(item.size() - 1).get(4));
                    double confirm = 1;
                    for (List<Double> doubles : item) {
                        if (max.compareTo(new BigDecimal(doubles.get(2))) < 0) {
                            max = new BigDecimal(doubles.get(2));
                        }
                        if (min.compareTo(new BigDecimal(doubles.get(3))) > 0) {
                            min = new BigDecimal(doubles.get(3));
                        }
                        vol = vol.add(new BigDecimal(doubles.get(5)));
                        volCcy = volCcy.add(new BigDecimal(doubles.get(6)));
                        volCcyQuote = volCcyQuote.add(new BigDecimal(doubles.get(7)));
                    }
                    return List.of(item.get(0).get(0),
                            open.setScale(2, RoundingMode.HALF_UP).doubleValue(),
                            max.setScale(2, RoundingMode.HALF_UP).doubleValue(),
                            min.setScale(2, RoundingMode.HALF_UP).doubleValue(),
                            close.setScale(2, RoundingMode.HALF_UP).doubleValue(),
                            vol.setScale(2, RoundingMode.HALF_UP).doubleValue(),
                            volCcy.setScale(2, RoundingMode.HALF_UP).doubleValue(),
                            volCcyQuote.setScale(2, RoundingMode.HALF_UP).doubleValue(),
                            confirm);
                }).collect(Collectors.toList());
    }

}
